/*
 * Alc5623.cpp
 *
 *  Created on: 2019年3月28日
 *      Author: westl
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <errno.h>
#include <sys/ioctl.h>
#include "alc5623.h"

#define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \
	| ALC5623_PWR_ADD2_DAC_REF_CIR)

#define ALC5623_ADD3_POWER_EN (ALC5623_PWR_ADD3_MAIN_BIAS \
	| ALC5623_PWR_ADD3_MIC1_BOOST_AD)

#define ALC5623_ADD1_POWER_EN \
	(ALC5623_PWR_ADD1_SHORT_CURR_DET_EN | ALC5623_PWR_ADD1_SOFTGEN_EN \
	| ALC5623_PWR_ADD1_DEPOP_BUF_HP | ALC5623_PWR_ADD1_HP_OUT_AMP \
	| ALC5623_PWR_ADD1_HP_OUT_ENH_AMP)

#define ALC5623_ADD1_POWER_AUX_EN \
	(ALC5623_PWR_ADD1_AUX_OUT_AMP | ALC5623_PWR_ADD1_AUX_OUT_ENH_AMP)

#define ALC5623_ADD1_POWER_EN_5622 \
	(ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
	| ALC5623_PWR_ADD1_HP_OUT_AMP)

Alc5623::Alc5623()
{
	// TODO Auto-generated constructor stub
	this->id = 0;
}

int Alc5623::codec_init()
{
	unsigned int vid1, vid2;
	int ret = regmap_read(ALC5623_VENDOR_ID1, &vid1);
	if (ret < 0) {
		printf("failed to read vendor ID1: %d\n", ret);
		return ret;
	}
	ret = regmap_read(ALC5623_VENDOR_ID2, &vid2);
	if (ret < 0) {
		printf("failed to read vendor ID2: %d\n", ret);
		return ret;
	}
	vid2 >>= 8;
	this->id = vid2;
	if(vid1 != 0x10ec){
		printf("unknown or wrong codec, expected %x, got %x\n", 0x10ec, vid1);
	}else{
		switch(vid2){
		case 0x21:
			printf("find codec: %s\n", "alc5621-hifi");
			break;
		case 0x22:
			printf("find codec: %s\n", "alc5622-hifi");
			break;
		case 0x23:
			printf("find codec: %s\n", "alc5623-hifi");
			break;
		default:
			return -1;
		}
	}
	printf("alc5623_init start, reset first...\n");
	regmap_write(ALC5623_RESET, 0);
	sleep(1);
	/* GPIO 配置 */
	regmap_write(ALC5623_GPIO_PIN_CONFIG,  0x1C0C);
	regmap_write(ALC5623_GPIO_PIN_SHARING, 0xC000);
	//regmap_write(ALC5623_GPIO_PIN_STICKY,  0x000C);		// STICKY
	regmap_write(ALC5623_GPIO_PIN_STICKY,  0x0000);		// NOT STICKY
	regmap_write(ALC5623_GPIO_PIN_WAKEUP,  0x000C);		// WAKE-UP
	regmap_write(ALC5623_GPIO_OUTPUT_PIN_CTRL,  0x0002);	// GPIO输出高
	//regmap_write(ALC5623_GPIO_OUTPUT_PIN_CTRL,  0x0000);	// GPIO输出低

	this->add_ctrl = 0xd320;		// modify by Hans
	this->jack_det_ctrl = 0xc610;		//gpio_high: AUX  gpio_low: HP
	//this->jack_det_ctrl = 0xc770;		//gpio_high/gpio_low: AUX HP
	regmap_write(ALC5623_ADD_CTRL_REG,  this->add_ctrl);
	regmap_write(ALC5623_JACK_DET_CTRL, this->jack_det_ctrl);
	
	
	/* 电源使能 */
	regmap_write(ALC5623_PWR_MANAG_ADD3, 0x8000);		// Power On Main Bias
	regmap_write(ALC5623_MONO_AUX_OUT_VOL, 0x8080);		// AUX L/R MUTE
	regmap_write(ALC5623_HP_OUT_VOL, 0x8080);			// HP L/R MUTE
	regmap_update_bits(ALC5623_PWR_MANAG_ADD1, ALC5623_PWR_ADD1_SOFTGEN_EN, ALC5623_PWR_ADD1_SOFTGEN_EN);	// Power On Softgen
	regmap_write(ALC5623_PWR_MANAG_ADD2, 0x3730);		// Power on Vref
	regmap_write(ALC5623_PWR_MANAG_ADD3, 0xE600);		// Power On AUX L/R Vol and HP L/R Vol
	/* 使能DAC */
	regmap_write(0x0C, 0x6606);		// 仅使能HPMUX  modify by Hans ex:0x6808
	regmap_write(0x1C, 0x1340);		// 配置HP AUX为HPMUX 
	/* 时钟参数配置 */
	if (this->id == 0x22)
		regmap_write(ALC5623_PWR_MANAG_ADD1, ALC5623_ADD1_POWER_EN_5622);
	else
		regmap_write(ALC5623_PWR_MANAG_ADD1, ALC5623_ADD1_POWER_EN | ALC5623_PWR_ADD1_MAIN_I2S_EN | ALC5623_ADD1_POWER_AUX_EN);
	
	/* 配置主从 */
	regmap_write(ALC5623_DAI_CONTROL, ALC5623_DAI_SDP_SLAVE_MODE | ALC5623_DAI_I2S_DF_I2S);				// ALC5623_DAI_CONTROL[0x34]
	/* 配置采样频率 */
	regmap_write(ALC5623_DAI_CONTROL, 0x8000);
	/* 设置PLL */
	regmap_write(0x42,  0xC000);		// BLCK->PLL->SYSCLK
	regmap_write(0x44,  0x1E00);		// FOUT = MCLK*16/4
	/* 关闭ADC */
	regmap_write(0x14, 0x7F7F);
	/* 关闭MIC */
	regmap_write(0x10, 0xE0E0);
	/* 关闭LINE_IN */
	regmap_write(0x0A, 0xE808);
	/* 关闭AUXIN */
	regmap_write(0x08, 0xE808);

	regmap_update_bits(ALC5623_MISC_CTRL, ALC5623_MISC_HP_DEPOP_MODE2_EN, ALC5623_MISC_HP_DEPOP_MODE2_EN);				//Enable HP Depop2
	regmap_update_bits(ALC5623_MISC_CTRL, ALC5623_MISC_AUXOUT_DEPOP_MODE2_EN, ALC5623_MISC_AUXOUT_DEPOP_MODE2_EN);		//Enable AUX Depop2
	/* disable at de-pop */
	regmap_update_bits(ALC5623_PWR_MANAG_ADD1, ALC5623_PWR_ADD1_HP_OUT_AMP, 0);
	regmap_update_bits(ALC5623_PWR_MANAG_ADD1, ALC5623_PWR_ADD1_HP_OUT_ENH_AMP, 0);
	regmap_write(ALC5623_HP_OUT_VOL, 0x8080);			// HP L/R MUTE
	usleep(300*1000);
	/* enable HP Depop2 ; disable AUX Depop2 */
	regmap_update_bits(ALC5623_PWR_MANAG_ADD1, ALC5623_PWR_ADD1_HP_OUT_AMP, ALC5623_PWR_ADD1_HP_OUT_AMP);
	regmap_update_bits(ALC5623_PWR_MANAG_ADD1, ALC5623_PWR_ADD1_HP_OUT_ENH_AMP, ALC5623_PWR_ADD1_HP_OUT_ENH_AMP);
	int i=0;
	int value = 0x1f;
	for(i=0; i<9; i++){
		usleep(70*1000);
		regmap_write(ALC5623_HP_OUT_VOL, (value<<8)|value);				// HPOUT[0x04] 音量 无衰减
		value -=3;
		if(value <= 0)
			value = 0;
	}
	/* 配置音量 */
	regmap_write(ALC5623_MONO_AUX_OUT_VOL, 0x0000);			// MONO/AUX[0x06] 音量 无衰减
	//regmap_write(ALC5623_HP_OUT_VOL, 0x0000);				// HPOUT[0x04] 音量 无衰减
	// disable HP Depop2
	regmap_update_bits(ALC5623_MISC_CTRL, ALC5623_MISC_HP_DEPOP_MODE2_EN, 0);
	regmap_update_bits(ALC5623_MISC_CTRL, ALC5623_MISC_AUXOUT_DEPOP_MODE2_EN, 0);

	/* 使能DAC 默认值 */
	//alc5623_snd_soc_write(codec, 0x62, (0<<11));
	/* gpio 输出低电平 */
	sleep(1);
	regmap_write(ALC5623_GPIO_OUTPUT_PIN_CTRL,  0x0000);	// GPIO输出低
	
	return 0;
}

int Alc5623::regmap_read(unsigned int reg, unsigned int* val)
{
	unsigned char i2c_rbuf[8];
	int ret;
	ret = ql_get_i2c_register(ALC5623_ADDR, reg, i2c_rbuf, 2);
	if(ret == 0){
		*val = (i2c_rbuf[0]<<8) | i2c_rbuf[1];
	}else{
		*val = 0;
	}
	return ret;
}

int Alc5623::regmap_write(unsigned int reg, unsigned int val)
{
	unsigned char i2c_wbuf[8];
	int ret;
	i2c_wbuf[1] = (val & 0xFF);
	i2c_wbuf[0] = (val & 0xFF00) >> 8;
	ret = ql_set_i2c_register(ALC5623_ADDR, reg, i2c_wbuf, 2);
	return ret;
}

int Alc5623::regmap_update_bits(unsigned int reg, unsigned int mask,
		unsigned int value)
{
	//int ret;
	unsigned int tmp, orig;
	regmap_read(reg,  &orig);
	tmp = orig & ~mask;
	tmp |= value & mask;
	return regmap_write(reg, tmp);
}

int Alc5623::get_i2c_register(int file, unsigned char addr, unsigned char reg,
		unsigned char* val, int len)
{
	unsigned char outbuf;
	struct i2c_rdwr_ioctl_data packets;
	struct i2c_msg messages[2];

	/*
	 * In order to read a register, we first do a "dummy write" by writing
	 * 0 bytes to the register we want to read from.  This is similar to
	 * the packet in set_i2c_register, except it's 1 byte rather than 2.
	 */
	outbuf = reg;
	messages[0].addr  = addr;
	messages[0].flags = 0;
	messages[0].len   = sizeof(outbuf);
	messages[0].buf   = &outbuf;

	/* The data will get returned in this structure */
	messages[1].addr  = addr;
	messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
	messages[1].len   = len;
	messages[1].buf   = val;

	/* Send the request to the kernel and get the result back */
	packets.msgs      = messages;
	packets.nmsgs     = 2;
	if(ioctl(file, I2C_RDWR, &packets) < 0) {
		printf("i2c[%x]_register[%x] get err: %s\n", addr, reg, strerror(errno));
		return 1;
	}
	//*val = inbuf;

	return 0;
}

int Alc5623::set_i2c_register(int file, unsigned char addr, unsigned char reg,
		unsigned char* value, int len)
{
	unsigned char *outbuf = (unsigned char *)malloc(sizeof(unsigned char)*(len+1));
	if(outbuf==NULL)
	{
		perror("MALLOC");
		return -1;
	}
	struct i2c_rdwr_ioctl_data packets;
	struct i2c_msg messages[1];

	messages[0].addr  = addr;
	messages[0].flags = 0;
	messages[0].len   = len+1;
	messages[0].buf   = outbuf;


	/* The first byte indicates which register we'll write */
	outbuf[0] = reg;

	/*
	 * The second byte indicates the value to write.  Note that for many
	 * devices, we can write multiple, sequential registers at once by
	 * simply making outbuf bigger.
	 */
	//    outbuf[1] = value;
	memcpy(outbuf+1, value, len);

	/* Transfer the i2c packets to the kernel and verify it worked */
	packets.msgs  = messages;
	packets.nmsgs = 1;
	if(ioctl(file, I2C_RDWR, &packets) < 0) {
		printf("i2c[%x]_register[%x] set err: %s\n", addr, reg, strerror(errno));
		free(outbuf);
		return 1;
	}

	free(outbuf);
	return 0;
}

int Alc5623::ql_get_i2c_register(unsigned char slave_addr, unsigned char reg,
		unsigned char* outbuf, int buf_len)
{
	int fd, ret;
	// Open a connection to the I2C userspace control file.
	if ((fd = open(I2C_FILE_NAME, O_RDWR)) < 0) {
		perror("Unable to open i2c control file");
		return -1;
	}
	ret = get_i2c_register(fd, slave_addr, reg, outbuf, buf_len);
	if(ret != 0)
	{
		printf("get_i2c[%x]_register[%x] ERR: %d\n", slave_addr, reg, ret);
		return -2;
	}else{
		//printf("get_i2c[%x]_register[%x] ok: %x\n", slave_addr, reg, (outbuf[0]<<8)|outbuf[1]);
	}
	close(fd);

	return 0;
}

int Alc5623::ql_set_i2c_register(unsigned char slave_addr, unsigned char reg,
		unsigned char* inbuf, int buf_len)
{
	int fd, ret;
	// Open a connection to the I2C userspace control file.
	if ((fd = open(I2C_FILE_NAME, O_RDWR)) < 0) {
		perror("Unable to open i2c control file");
		return -1;
	}
	ret = set_i2c_register(fd, slave_addr, reg, inbuf, buf_len);
	if(ret != 0)
	{
		printf("set_i2c[%x]_register[%x]-[%x] ERR: %d\n", slave_addr, reg, (inbuf[0]<<8)|inbuf[1], ret);
		return -4;
	}else{
		//printf("set_i2c[%x]_register[%x] ok: %x\n", slave_addr, reg, (inbuf[0]<<8)|inbuf[1]);
	}
	close(fd);
	return 0;
}

int Alc5623::mute(void)
{
	int ret;

	/* gpio 输出高电平 */
	regmap_write(ALC5623_GPIO_OUTPUT_PIN_CTRL,  0x0002);	// GPIO输出高

	ret = regmap_write(ALC5623_RESET, 0);
	if (ret < 0) {
		printf("failed to reset ALC5623");
		return ret;
	}
	ret = regmap_write(ALC5623_HP_OUT_VOL, 0x8080);
	if (ret < 0) {
		printf("failed to mute ALC5623");
		return ret;
	}
	return 0;
}

Alc5623::~Alc5623()
{
	// TODO Auto-generated destructor stub
}

